#include <cassert>
#include <stack>
#include <osg/Vec3>
#include <osg/io_utils>
#include <alut.h>
#include <iostream>
#include "audiomanager.h"
#include "datatypedefine.h"

osg::ref_ptr<OpenAudio::AudioManager> OpenAudio::AudioManager::_audioMgr(NULL);
osg::ref_ptr<OpenAudio::Listener> OpenAudio::AudioManager::_listener(NULL);
const char* OpenAudio::AudioManager::_EaxVer = "EAX2.0";
const char* OpenAudio::AudioManager::_EaxSet = "EAXSet";
const char* OpenAudio::AudioManager::_EaxGet = "EAXGet";

namespace OpenAudio 
{
	AudioManager::AudioManager(CoreBase::Scene* scene,const QString& name,ALCdevice* dev, ALCcontext* cntxt, bool shutdownPassedInContexts)
		: CoreBase::Base(name), _EAXSet(NULL), _EAXGet(NULL), _numSounds(0), _isConfigured(false)
		, _device(NULL), _context(NULL), _shutdownContexts(false),_mainScene(scene)
	{
		addSender(_mainScene);
		if (dev == NULL && cntxt == NULL)
		{
			if (alutInit(NULL, NULL) == AL_FALSE)
			{
				std::cout << "Error initializing alut" << std::cout;
			}
			//_context = alcGetCurrentContext();
			//_device = alcGetContextsDevice(_context);
		}
		else
		{
			if(alutInitWithoutContext(NULL, NULL) == AL_FALSE)
			{
				std::cout << "Error initializing alut" << std::cout;
			}
			_device = dev;
			_context = cntxt;
			_shutdownContexts = shutdownPassedInContexts;
		}
		_audioMode = Listen;
	}

	AudioManager::~AudioManager()
	{}

	void AudioManager::Instantiate(CoreBase::Scene* scene,const QString& name, ALCdevice* dev, ALCcontext* cntxt, bool shutdownPassedInContexts)
	{
		if (_audioMgr.get())
		{
			return;
		}
		_audioMgr  = new AudioManager(scene,name, dev, cntxt, shutdownPassedInContexts);
		assert(_audioMgr.get());
		_listener  = new Listener(scene);
		assert(_listener.get());
	}

	void AudioManager::Destroy()
	{
		SoundVector::iterator it;
		for (it = _soundList.begin(); it != _soundList.end(); ++it)
		{
			releaseSoundSource(**it, "Error freeing source in Audio Manager destructor.",
				__FUNCTION__, __LINE__);
		}
		BufferData* bd(NULL);
		for (BufferMap::iterator iter(_bufferMap.begin()); iter != _bufferMap.end(); iter++)
		{
			if ((bd = iter->second) == NULL)
			{
				continue;
			}
			iter->second = NULL;
			releaseSoundBuffer(bd->buf, "alDeleteBuffers(1, &bd->buf )", __FUNCTION__, __LINE__);
			delete bd;
		}
		_bufferMap.clear();
		_soundList.clear();
		//alutSleep(1);
		alutExit();
		//if (_shutdownContexts)
		//{
		//	closeContext();
		//	closeDevice();
		//}
		removeSender(_mainScene);
		_listener = NULL;
		_audioMgr = NULL;
	}

	void AudioManager::setOpenALDevice(const ALCchar* deviceName)
	{
		closeContext();
		closeDevice();
		openDevice(deviceName);
		createContext();
	}

	AudioManager& AudioManager::GetInstance()
	{
		return *_audioMgr;
	}

	Listener* AudioManager::GetListener()
	{
		return static_cast<Listener*>(_listener.get());
	}

	void AudioManager::setDistanceModel(ALenum dm)
	{
		alDistanceModel(dm);
	}

	void setDopplerFactor(float f)
	{
		alDopplerFactor(f);
	}

	void setSpeedOfSound(float s)
	{
		alSpeedOfSound(s);
	}

	void AudioManager::onMessage(MessageData* data)
	{
		assert(data);
		//void* userData;
		//double userData[2] = { time1, time2 };
		//data = *static_cast<const double*>(data->userData)
		if (data->message == SYSTEM_MESSAGE_PRE_FRAME)
		{
			preFrame(0);
			return;
		}
		if (data->message == SYSTEM_MESSAGE_PAUSE)
		{
			preFrame(0);
			return;
		}
		if (data->message == SYSTEM_MESSAGE_PAUSE_START)
		{
			_soundStateMap.clear();
			for (SoundVector::iterator iter = _soundList.begin(); iter != _soundList.end(); ++iter)
			{
				Sound* sob = iter->get();
				if (sob == NULL)
				{
					continue;
				}
				if (sob->isPaused())
				{
					_soundStateMap.insert(SoundObjectStateMap::value_type(sob, PAUSED));
				}
				else if (sob->isPlaying())
				{
					_soundStateMap.insert(SoundObjectStateMap::value_type(sob, PLAYING));
				}
				else if (sob->isStopped())
				{
					_soundStateMap.insert(SoundObjectStateMap::value_type(sob, STOPPED));
				}
				sob->pauseImmediately();
			}
		}
		if (data->message == SYSTEM_MESSAGE_PAUSE_END)
		{
			for (SoundVector::iterator iter = _soundList.begin(); iter != _soundList.end(); ++iter)
			{
				Sound* sob = iter->get();
				switch (_soundStateMap[sob])
				{
				case PAUSED:
					//No need to do anything.
					break;
				case PLAYING:
					sob->play();
					break;
				case STOPPED:
					sob->stop();
					break;
				default:
					break;
				}
			}
		}
	}

	Sound* AudioManager::newSound(bool addintomanager)
	{
		osg::ref_ptr<Sound> snd(NULL);
		if (snd.get() == NULL)
		{
			snd = new Sound(NULL);
			assert(snd.get());
			snd->setSoundMode((unsigned int)_audioMode);
		}
		//AddSender(snd.get());
		if(addintomanager)
			_soundList.push_back(snd);
		return snd.release();
	}

	void AudioManager::addIntoManager(Sound* sound)
	{
		SoundVector::iterator iter;
		for (iter = _soundList.begin(); iter != _soundList.end(); ++iter)
		{
			if (sound == *iter)
			{
				return;
			}
		}
		_soundList.push_back(sound);
	}

	void AudioManager::freeSound(Sound* sound)
	{
		osg::ref_ptr<Sound> snd = static_cast<Sound*>(sound);
		if (snd.get() == NULL)
		{
			return;
		}
		SoundVector::iterator iter;
		for (iter = _soundList.begin(); iter != _soundList.end(); ++iter)
		{
			if (snd != *iter)
			{
				continue;
			}
			_soundList.erase(iter);
			break;
		}
		//snd->RemoveSender(this);
		//snd->removeSender(&dtCore::System::GetInstance());
		//RemoveSender(snd.get());
		// free the sound's source and buffer
		unloadSound(snd.get());
		snd->clear();
	}

	ALint AudioManager::loadFile(const std::string& file)
	{
		if (file.empty())
		{
			return AL_NONE;
		}
		BufferData* bd = _bufferMap[file];
		if (bd != 0)
		{
			return bd->buf;
		}
		bd = new BufferData;
		//alGenBuffers(1L, &bd->buf);
		//ALenum format(0);
		//ALsizei size(0);
		//ALvoid* data = NULL;
		//// NON-DEPRECATED version for ALUT >= 1.0.0
		//ALfloat freq(0);
		//data = alutLoadMemoryFromFile(file.c_str(), &format, &size, &freq);
		bd->buf = alutCreateBufferFromFile (file.c_str());
		if (bd->buf == AL_NONE)
		{
			//releaseSoundBuffer(bd->buf, "alDeleteBuffers error", __FUNCTION__, __LINE__);
			delete bd;
			return AL_NONE;
		}
		//bd->format = format;
		//bd->freq   = ALsizei(freq);
		//bd->size   = size;
		//alBufferData(bd->buf, bd->format, data, bd->size, bd->freq);
				ALenum error = alGetError();
	   if (error != AL_NO_ERROR)
	   {
		   std::string errorstr = alGetString(error);
	   }
		//free(data);
		//data = NULL;
		_bufferMap[file] = bd;
		bd->file = _bufferMap.find(file)->first.c_str();
		return bd->buf;
	}

	bool AudioManager::unloadFile(const std::string& file)
	{
		if (file.empty())
		{
			return false;
		}
		BufferMap::iterator iter = _bufferMap.find(file);
		if (iter == _bufferMap.end())
		{
			return false;
		}
		BufferData* bd = iter->second;
		if (bd == 0)
		{
			_bufferMap.erase(iter);
			return false;
		}
		if (bd->use)
		{
			return false;
		}
		releaseSoundBuffer(bd->buf, "alDeleteBuffers( 1L, &bd->buf );", __FUNCTION__, __LINE__);
		delete bd;
		_bufferMap.erase(iter);
		return true;
	}

	void AudioManager::preFrame(const double deltaFrameTime)
	{
		osg::ref_ptr<Sound> snd(NULL);
		for (unsigned int i = 0; i < _soundList.size(); ++i)
		{
			snd = _soundList.at(i);
			if (snd.get() == NULL)
			{
				continue;
			}
			//snd->SetPositionFromParent();
			//snd->SetDirectionFromParent();
			snd->runAllCommandsInQueue();
		}
	}

	void AudioManager::switchAudioMode()
	{
		if(_audioMode == Listen)
			setAudioMode(Draw);
		else
		{
			osg::ref_ptr<Sound> snd(NULL);
			for (unsigned int i = 0; i < _soundList.size(); ++i)
			{
				snd = _soundList.at(i);
				if (snd.get() == NULL)
				{
					continue;
				}
				loadSoundBuffer(*snd);
			}
			setAudioMode(Listen);
		}
	}

	void AudioManager::soundFileChanged(Sound* sound)
	{
		SoundVector::iterator iter;
		for (iter = _soundList.begin(); iter != _soundList.end(); ++iter)
		{
			if (sound == *iter)
			{
				if(_audioMode == Listen)
				{
					loadSoundBuffer(*sound);
				}
			}
		}
	}

	void AudioManager::setAudioMode(AudioManager::AudioMode mode)
	{
		if(_audioMode != mode)
		{
			osg::ref_ptr<Sound> snd(NULL);
			_audioMode = mode;
			for (unsigned int i = 0; i < _soundList.size(); ++i)
			{
				snd = _soundList.at(i);
				if (snd.get() == NULL)
				{
					continue;
				}
				snd->setSoundMode((unsigned int)_audioMode);
			}
		}
	}

	AudioManager::AudioMode AudioManager::getAudioMode()
	{
		return _audioMode;
	}

	bool AudioManager::configured() const
	{
		return _isConfigured;
	}

	bool AudioManager::configEAX(bool eax)
	{
		if (!eax)
		{
			return false;
		}
#ifndef AL_VERSION_1_1
		ALubyte buf[32L];
		memset( buf, 0L, 32L );
		memcpy( buf, _EaxVer, std::min( strlen(_EaxVer), size_t(32L) ) );
#else
		const ALchar* buf = _EaxVer;
#endif
		if (alIsExtensionPresent(buf) == AL_FALSE)
		{
			return false;
		}
#ifndef AL_VERSION_1_1
		memset(buf, 0L, 32L);
		memcpy(buf, _EaxSet, std::min( strlen(_EaxSet), size_t(32L)));
#else
		buf = _EaxSet;
#endif
		_EAXSet = alGetProcAddress(buf);
		if (_EAXSet == 0)
		{
			return false;
		}
#ifndef AL_VERSION_1_1
		memset(buf, 0, 32);
		memcpy(buf, _EaxGet, std::min( strlen(_EaxGet), size_t(32)));
#else
		buf = _EaxVer;
#endif
		_EAXGet = alGetProcAddress(buf);
		if (_EAXGet == 0)
		{
			_EAXSet = 0;
			return false;
		}
		return true;
	}

	int AudioManager::loadSoundBuffer(Sound& snd)
	{
		const char* file = snd.getFilename();
		int useCount = 0;
		if (file != NULL)
		{
			if (loadFile(file) != AL_NONE)
			{
				BufferData* bd = _bufferMap[file];
				snd.setBuffer(bd->buf);
				useCount = (++bd->use);
			}
			else
			{
				std::cout << "Error Load File" << std::cout;
			}
		}
		return useCount;
	}

	int AudioManager::unloadSound(Sound* snd)
	{
		assert(snd);
		int useCount = -1;
		const char* file = snd->getFilename();
		if (file == NULL)
		{
			return useCount;
		}
		snd->setBuffer(AL_NONE);
		BufferData* bd = _bufferMap[file];
		if (bd == NULL)
		{
			return useCount;
		}
		if (bd->use > 0)
		{
			useCount = (--bd->use);
		}
		else
		{
			std::cout << "Error unloadSound" << std::cout;
			useCount = bd->use = 0;
		}
		if (useCount == 0)
		{
			releaseSoundSource(*snd, "Sound source delete error", __FUNCTION__, __LINE__);
		}
		unloadFile(file);
		return useCount;
	}

	bool AudioManager::releaseSoundSource(Sound& snd, const std::string& errorMessage,
		const std::string& callerFunctionName, int callerFunctionLineNum )
	{
		return snd.releaseSource(); 
	}

	bool AudioManager::releaseSoundBuffer(ALuint bufferHandle, const std::string& errorMessage,
		const std::string& callerFunctionName, int callerFunctionLineNum )
	{
		bool success = false;
		if (alIsBuffer(bufferHandle) == AL_TRUE)
		{
			alDeleteBuffers(1L, &bufferHandle);
			success = true;
		}
		return success;
	}

	void AudioManager::openDevice(const ALCchar* deviceName)
	{
		_device = alcOpenDevice(deviceName);
		if (_device == NULL)
		{
			std::cout << "Error openDevice" << std::cout;
		}
	}

	void AudioManager::createContext()
	{
		_context = alcCreateContext(_device, NULL);
		if (!_context)
		{
			std::cout << "Error createContext" << std::cout;
		}
	}

	void AudioManager::closeDevice()
	{
		if (_device)
		{
			alcCloseDevice(_device);
			_device = NULL;
		}
	}

	void AudioManager::closeContext()
	{
		alcMakeContextCurrent(NULL);
		if (_context)
		{
			alcDestroyContext(_context);
			_context = NULL;
		}
	}
}